'======================================================================
'
'  File:		ChangeCase.cs
'
'  Summary:	This file contains the implementation of the ChangeCase transformation component.
'				This sample demonstrates how to write a transformation component
'				with a synchronous output. It changes the case of a character in a string to 
'				lower case or upper case. Changes are made to the columns 
'				as they pass through the component, it does not add any output columns to the data flow.
'
'  Date:		6/15/2004
'
'---------------------------------------------------------------------
'
'  This file is part of the Microsoft SQL Server Code Samples.
'  Copyright (C) Microsoft Corporation.  All rights reserved.
'
'	This source code is intended only as a supplement to Microsoft
'	Development Tools andor on-line documentation.  See these other
'	materials for detailed information regarding Microsoft code samples.
'
'	THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
'  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
'  THE IMPLIED WARRANTIES OF MERCHANTABILITY ANDOR FITNESS FOR A
'	PARTICULAR PURPOSE.
'
'===================================================================== 
Imports System
Imports System.Collections
Imports System.Globalization
Imports System.Runtime.InteropServices
Imports Microsoft.SqlServer.Dts.Pipeline
Imports Microsoft.SqlServer.Dts.Pipeline.Wrapper
Imports Microsoft.SqlServer.Dts.Runtime.Wrapper
Imports Microsoft.SqlServer.Dts.Runtime


<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")> _
<ComVisible(True), DtsPipelineComponent(DisplayName:="ChangeCaseVB", Description:="Changes the case of a character in a string.", IconResource:="Microsoft.Samples.SqlServer.Dts.ChangeCase.ico")> _
Public Class ChangeCase
    Inherits PipelineComponent

    Public Enum Operation
        ToUpper
        ToLower
    End Enum

    Private Structure ColumnInfo
        Public bufferColumnIndex As Integer
        Public characterIndex As Integer
        Public operation As Operation
        Public columnDisposition As DTSRowDisposition
        Public lineageID As Integer
    End Structure

    Const INVALIDCHARACTERINDEX As Integer = 1
    Const INVALIDCHARACTERINDEXMESSAGE As String = "The character index to change is outside the length of the column value."
    Private Cancel As Boolean
    Private columnInfos As ColumnInfo()

    Public Overloads Overrides Sub ProvideComponentProperties()
        ComponentMetaData.UsesDispositions = True
        Dim input As IDTSInput100 = ComponentMetaData.InputCollection.New
        input.Name = "ChangeCaseInput"
        input.ErrorRowDisposition = DTSRowDisposition.RD_FailComponent
        Dim output As IDTSOutput100 = ComponentMetaData.OutputCollection.New
        output.Name = "ChangeCaseOutput"
        output.SynchronousInputID = input.ID
        output.ExclusionGroup = 1
        AddErrorOutput("ChangeCaseErrorOutput", input.ID, output.ExclusionGroup)
    End Sub

    <CLSCompliant(False)> _
    Public Overloads Overrides Function Validate() As DTSValidationStatus
        If ComponentMetaData.AreInputColumnsValid = False Then
            Return DTSValidationStatus.VS_NEEDSNEWMETADATA
        End If
        For Each column As IDTSInputColumn100 In ComponentMetaData.InputCollection(0).InputColumnCollection
            If Not (column.UsageType = DTSUsageType.UT_READWRITE) Then
                ComponentMetaData.FireError(0, column.IdentificationString, "Input columns must be set to READWRITE.", "", 0, Cancel)
                Return DTSValidationStatus.VS_ISBROKEN
            End If
            If Not (column.DataType = DataType.DT_WSTR) AndAlso Not (column.DataType = DataType.DT_STR) AndAlso Not (column.DataType = DataType.DT_TEXT) Then
                ComponentMetaData.FireError(HResults.DTS_E_INVALIDDATATYPE, ComponentMetaData.Name, "The DataType " + column.DataType.ToString + " of " + column.IdentificationString + " is not supported by the " + ComponentMetaData.Name + " component.", "", 0, Cancel)
                Return DTSValidationStatus.VS_ISBROKEN
            End If
        Next
        Return MyBase.Validate
    End Function

    Public Overloads Overrides Sub ReinitializeMetaData()
        Me.ComponentMetaData.RemoveInvalidInputColumns()
        MyBase.ReinitializeMetaData()
    End Sub

    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")> _
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId:="System.Exception.#ctor(System.String)")> _
    <CLSCompliant(False)> _
    Public Overloads Overrides Function SetUsageType(ByVal inputID As Integer, ByVal virtualInput As IDTSVirtualInput100, ByVal lineageID As Integer, ByVal usageType As DTSUsageType) As IDTSInputColumn100
        If usageType = DTSUsageType.UT_READONLY Then
            Throw New Exception("The UsageType must be set to ReadWrite.")
        End If
        Dim col As IDTSInputColumn100 = MyBase.SetUsageType(inputID, virtualInput, lineageID, usageType)
        If Not (col Is Nothing) Then
            col.ErrorRowDisposition = DTSRowDisposition.RD_NotUsed
            col.ErrorOrTruncationOperation = "Error, when CharacterIndex exceeds the length of the column."
            Dim prop As IDTSCustomProperty100 = col.CustomPropertyCollection.New
            prop.Name = "CharacterIndex"
            prop.Value = 0
            Dim op As IDTSCustomProperty100 = col.CustomPropertyCollection.New
            op.Name = "Operation"
            op.Value = Operation.ToUpper
        End If
        Return col
    End Function

    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")> _
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId:="System.Exception.#ctor(System.String)")> _
    Public Overloads Overrides Sub DeleteOutput(ByVal outputID As Integer)
        Throw New Exception("Can't delete output " + outputID.ToString(System.Globalization.CultureInfo.InvariantCulture))
    End Sub

    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")> _
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId:="System.Exception.#ctor(System.String)")> _
    <CLSCompliant(False)> _
    Public Overloads Overrides Function InsertOutput(ByVal insertPlacement As DTSInsertPlacement, ByVal outputID As Integer) As IDTSOutput100
        Throw New Exception("Can't add output to the component.")
    End Function

    Public Overloads Overrides Sub PreExecute()
        Dim input As IDTSInput100 = ComponentMetaData.InputCollection(0)
        columnInfos = New ColumnInfo(input.InputColumnCollection.Count - 1) {}
        Dim x As Integer = 0
        While x < input.InputColumnCollection.Count
            Dim column As IDTSInputColumn100 = input.InputColumnCollection(x)
            columnInfos(x) = New ColumnInfo
            columnInfos(x).bufferColumnIndex = BufferManager.FindColumnByLineageID(input.Buffer, column.LineageID)
            columnInfos(x).characterIndex = CType(column.CustomPropertyCollection("CharacterIndex").Value, Integer)
            columnInfos(x).operation = CType(column.CustomPropertyCollection("Operation").Value, Operation)
            columnInfos(x).columnDisposition = column.ErrorRowDisposition
            columnInfos(x).lineageID = column.LineageID
            System.Math.Min(System.Threading.Interlocked.Increment(x), x - 1)
        End While
    End Sub

    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")> _
    <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId:="System.Exception.#ctor(System.String)")> _
    Public Overloads Overrides Sub ProcessInput(ByVal inputID As Integer, ByVal buffer As PipelineBuffer)
        If buffer Is Nothing Then
            Throw New ArgumentNullException("buffer")
        End If

        If Not buffer.EndOfRowset Then
            Dim input As IDTSInput100 = ComponentMetaData.InputCollection.GetObjectByID(inputID)
            Dim errorOutputID As Integer = -1
            Dim errorOutputIndex As Integer = -1
            Dim defaultOutputId As Integer = -1
            GetErrorOutputInfo(errorOutputID, errorOutputIndex)
            If errorOutputIndex = 0 Then
                defaultOutputId = ComponentMetaData.OutputCollection(1).ID
            Else
                defaultOutputId = ComponentMetaData.OutputCollection(0).ID
            End If
            While buffer.NextRow
                If columnInfos.Length = 0 Then
                    buffer.DirectRow(defaultOutputId)
                End If
                Dim isError As Boolean = False
                Dim x As Integer = 0
                While x < columnInfos.Length
                    Dim colInfo As ColumnInfo = columnInfos(x)
                    If Not buffer.IsNull(colInfo.bufferColumnIndex) Then
                        Dim columnValue As String = buffer.GetString(colInfo.bufferColumnIndex)
                        If columnValue.Length <= colInfo.characterIndex Then
                            If input.ErrorRowDisposition = DTSRowDisposition.RD_FailComponent Then
                                Throw New Exception("Failing component because the CharacterIndex is greater than the length of the column.")
                            Else
                                If input.ErrorRowDisposition = DTSRowDisposition.RD_RedirectRow Then
                                    buffer.DirectErrorRow(errorOutputID, ChangeCase.INVALIDCHARACTERINDEX, colInfo.lineageID)
                                    isError = True
                                    ' break 
                                End If
                            End If
                        Else
                            If colInfo.operation = Operation.ToUpper Then
                                columnValue = columnValue.Substring(0, colInfo.characterIndex) + columnValue.Substring(colInfo.characterIndex, 1).ToUpper(System.Globalization.CultureInfo.InvariantCulture) + columnValue.Substring(colInfo.characterIndex + 1)
                            Else
                                columnValue = columnValue.Substring(0, colInfo.characterIndex) + columnValue.Substring(colInfo.characterIndex, 1).ToLower(System.Globalization.CultureInfo.InvariantCulture) + columnValue.Substring(colInfo.characterIndex + 1)
                            End If
                            buffer.SetString(colInfo.bufferColumnIndex, columnValue)
                        End If
                    End If
                    System.Math.Min(System.Threading.Interlocked.Increment(x), x - 1)
                End While
                If Not isError Then
                    buffer.DirectRow(defaultOutputId)
                End If
            End While
        End If
    End Sub

    Public Overloads Overrides Function DescribeRedirectedErrorCode(ByVal iErrorCode As Integer) As String
        If iErrorCode = INVALIDCHARACTERINDEX Then
            Return INVALIDCHARACTERINDEXMESSAGE
        End If
        Return ""
    End Function
End Class